package jp.seraph.jsade.math;

import jp.seraph.jsade.util.GeometoryUtil;

/**
 * 角度を表わすクラスです。　このクラスが表す角度は(-180, 180]に正規化されています
 * このクラスのインスタンスはイミュータブルであり、内部状態(角度)が変更されることはありません。
 *
 */
public class Angle {
    private Angle(double aDegree){
        mDegree = GeometoryUtil.normalizeAngleAsDegree(aDegree);
    }

    /**
     * 引数をRadian単位の角度としてAngleのインスタンスを作成します。
     *
     * @param aRad
     * @return
     */
    public static Angle createAngleAsRad(double aRad){
        return new Angle(Math.toDegrees(aRad));
    }

    /**
     * 引数を1周を360度で表す度数としてAngleのインスタンスを作成します。
     *
     * @param aDegree
     * @return
     */
    public static Angle createAngleAsDegree(double aDegree){
        return new Angle(aDegree);
    }

    private double mDegree;

    public final static Angle ZERO = new Angle(0);
    public final static Angle PI = new Angle(180);

    /**
     * Radian単位で、このAngleが示す角度を返します。
     *
     * @return
     */
    public double getRad(){
        return GeometoryUtil.degreeToRad(mDegree);
    }

    /**
     * 度数表現で、このAngleが示す角度を返します。
     *
     * @return
     */
    public double getDegree(){
        return mDegree;
    }

    /**
     * このインスタンスの角度にaDgree度だけ足したAngleを返します。
     *
     * @param aDegree
     * @return
     */
    public Angle addDegree(double aDegree){
        return Angle.createAngleAsDegree(this.getDegree() + aDegree);
    }

    /**
     * このインスタンスの角度からaDgree度だけひいたAngleを返します。
     *
     * @param aDegree
     * @return
     */
    public Angle subtractDegree(double aDegree){
        return Angle.createAngleAsDegree(this.getDegree() - aDegree);
    }

    /**
     * このインスタンスの角度にaRad[rad]だけ足したAngleを返します。
     *
     * @param aRad
     * @return
     */
    public Angle addRad(double aRad){
        return Angle.createAngleAsRad(this.getRad() + aRad);
    }

    /**
     * このインスタンスの角度から、aRad[rad]だけ引いたAngleを返します。
     *
     * @param aRad
     * @return
     */
    public Angle subtractRad(double aRad){
        return Angle.createAngleAsRad(this.getRad() - aRad);
    }

    /**
     * このインスタンスの角度に引数の角度を足したAngleを返します。
     *
     * @param aAngle
     * @return
     */
    public Angle add(Angle aAngle){
        return this.addDegree(aAngle.getDegree());
    }

    /**
     * このインスタンスの角度から引数の角度をひいたAngleを返します。
     *
     * @param aAngle
     * @return
     */
    public Angle subtract(Angle aAngle){
        return this.subtractDegree(aAngle.getDegree());
    }

    /**
     * このAngleの正負を反転させた角度を返します。
     *
     * @return
     */
    public Angle minus(){
        return Angle.createAngleAsDegree(-mDegree);
    }

    /**
     * この角度が正の角度であるかどうかを返します。
     *
     * @return 正の角度であればtrue
     */
    public boolean isPositive(){
        return mDegree > 0;
    }

    /**
     * この角度の絶対値を示す角度を返します。
     *
     * @return
     */
    public Angle abs(){
        if(mDegree < 0)
            return this.minus();
        else
            return this;
    }

    /**
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    
    public boolean equals(Object aObj) {
        if(aObj instanceof Angle){
            Angle tAngle = (Angle)aObj;
            return tAngle.mDegree == this.mDegree;
        }else{
            return false;
        }
    }

    /**
     *
     * @see java.lang.Object#hashCode()
     */
    
    public int hashCode() {
        return (int)(Double.doubleToLongBits(mDegree) & 0xFFFFFFFF);
    }


}
